<?php

class OfferStoreProductOverlappingValidator extends sfValidatorSchema
{
  public function __construct($options = array(), $messages = array())
  {
    parent::__construct(null, $options, $messages);
  }
  
  protected function configure($options = array(), $messages = array())
  {
    $this->addOption('throw_global_error', true);
    $overlapping = 'Overlapping with general offer %name%.';
    $this->addMessage('general_overlapping', $overlapping);
    
    $overlapping = 'Overlapping with single offer %name% (%bottom% - %top%).';
    $this->addMessage('single_overlapping', $overlapping);
  }
  
  protected function doClean($values)
  {
    if (!is_array($values))
    {
      throw new InvalidArgumentException('You must pass an array parameter to the clean() method (this validator can only be used as a post validator).');
    }
    
    // not to validate when there is no store-product or discount type chosen
    if (!$values['store_product_id'] || !$values['discount_type_id'])
    {
      return $values;
    }
    
    $q = kcDoctrineQuery::create()   //$c = new Criteria();
         ->from('OfferStoreProduct o')
         ->where('o.store_product_id = ?',$values['store_product_id']);  //$c->add(OfferStoreProductPeer::ID_STORE_PRODUCT, $values['id_store_product']);
       
    if ($values['id'])
    {
      $q->addWhere('o.id <> ?', $values['id']); //$c->add(OfferStoreProductPeer::ID, $values['id'], Criteria::NOT_EQUAL);
    }
    $q->innerJoin('o.DiscountType d');
    $offers = $q->execute();  //$offers = OfferStoreProductPeer::doSelectJoinDiscountType($c);
    
    $discountType = Doctrine::getTable('DiscountType')->findOneById($values['discount_type_id']);  //$discountType = DiscountTypePeer::retrieveByPK($values['id_discount_type']);
    
    // not to validate if not general discount and there is no bottom qty or top qty, or if bottom > top
    $general_discount = in_array($discountType->getType(), array('GM', 'GP'));
    $conditions       = !$values['bottom_qty'] || !$values['top_qty'] || ($values['bottom_qty'] > $values['top_qty']);
    if (!$general_discount && $conditions)
    {
      return $values;
    }
    
    $errors = array();
    foreach ($offers as $offer)
    {
      $error = null;
      if (in_array($offer->getDiscountType()->getType(), array('GM', 'GP')))
      {
        $error = new sfValidatorError($this, 'general_overlapping', array('name' => $offer->getName()));
      }
      else
      {
        // if discount per price + free the offer's top is its top + free
        $offer_top = $offer->getDiscountType()->getType() == 'PF' ? $offer->getTopQty() + $offer->getFreeQty() : $offer->getTopQty();
        if ($general_discount || $this->isOverlapWith($values['bottom_qty'], $values['top_qty'], $offer->getBottomQty(), $offer_top))
        {
          $error = new sfValidatorError
                       (
                         $this, 
                         'single_overlapping', 
                         array
                         (
                           'name'   => $offer->getName(), 
                           'bottom' => $offer->getBottomQty(), 
                           'top'    => $offer_top
                         )
                       );
                       
        }
      }
      
      if ($error)
      {
        $errors[] = $error;
      }
    }
    
    if ($errors)
    {
      throw new sfValidatorErrorSchema($this, $errors);
    }
    
    return $values;
  }
  protected function isOverlapWith($bot, $top, $o_bot, $o_top)
  {
    /**
     * to avoid overlapping for bottom and top quantities,
     * all the other offer's bottom and top quantities must be or less or greater than both.
     */ 
    $isnt_overlap = ($bot < $o_bot && $top < $o_bot) || ($bot > $o_top && $top > $o_top);

    return !$isnt_overlap;
  }
}
